#ifndef _TLIB_TBIND_H_
#define _TLIB_TBIND_H_
//http://github.com/ToyAuthor/functional
namespace tlib
{
template<typename T> struct type
{
	typedef T value;
};

template<int I> struct Argc
{
};

template<typename T>
struct reference_wrapper
{
public:
	typedef T type;

	explicit reference_wrapper(T &t) :
		t_(&t)
	{
	}

	operator T&() const
	{
		return *t_;
	}
	T& get() const
	{
		return *t_;
	}
	T* get_pointer() const
	{
		return t_;
	}

private:
	T* t_;
};

template<typename T> inline T* get_pointer(T *o)
{
	return o;
}
template<typename T> inline T* get_pointer(T &o)
{
	return &o;
}

template<typename T> struct untie_ref
{
	typedef T type;
};
template<typename T> struct untie_ref<T&> : untie_ref<T>
{
};

template<typename T> struct param_traits
{
	typedef typename untie_ref<T>::type& type;
};

template<typename R> struct result_traits
{
	typedef R type;
};

template<typename F> struct result_traits<type<F> > : result_traits<typename F::result_type>
{
};

template<typename F>
struct result_traits<type<reference_wrapper<F> > > : result_traits<typename F::result_type>
{
};

template<typename T>
inline reference_wrapper<T> ref(T &t)
{
	return reference_wrapper<T> (t);
}

template<typename T>
inline reference_wrapper<T const> cref(const T &t)
{
	return reference_wrapper<const T> (t);
}

namespace placeholders
{

static inline Argc<1> _1()
{
	return tlib::Argc<1>();
}
static inline Argc<2> _2()
{
	return tlib::Argc<2>();
}
static inline Argc<3> _3()
{
	return tlib::Argc<3>();
}
static inline Argc<4> _4()
{
	return tlib::Argc<4>();
}

}

struct storage_base
{
	template<typename T>
	inline T& operator[](T &v) const
	{
		return v;
	}

	template<typename T>
	inline const T& operator[](const T &v) const
	{
		return v;
	}

	template<typename T>
	inline T& operator[](reference_wrapper<T> &v) const
	{
		return v.get();
	}

	template<typename T>
	inline T& operator[](const reference_wrapper<T> &v) const
	{
		return v.get();
	}
};

struct storage0: storage_base
{
	using storage_base::operator[];

	template<typename R, typename F, typename S>
	inline R operator()(type<R> , F &f, const S&) const
	{
		return f();
	}

	template<typename R, typename F>
	inline R Do(type<R> , F &f) const
	{
		return f();
	}
};

template<typename D>
struct storage1_base: storage0
{
	typedef storage0 base;

	using base::operator[];

	template<typename R, typename F, typename S>
	inline R operator()(type<R> , F &f, const S &s) const
	{
		const D &d = static_cast<const D&> (*this);
		return d[f](s[d.a1_]);
	}
};

template<typename A1>
struct storage1: storage1_base<storage1<A1> >
{
	typedef storage1_base<storage1<A1> > base;
	typedef typename param_traits<A1>::type P1;
	typedef typename result_traits<A1>::type result_type;

	storage1(P1 p1) :
		a1_(p1)
	{
	}

	using base::operator[];

	inline result_type operator[](Argc<1> (*)()) const
	{
		return a1_;
	}

	template<typename R, typename F>
	inline R Do(type<R> , F &f) const
	{
		return f(a1_);
	}

	A1 a1_;
};

template<int I>
struct storage1<Argc<I> (*)()> : storage1_base<storage1<Argc<I> (*)()> >
{
	typedef typename param_traits<Argc<I> (*)()>::type P1;

	storage1(P1)
	{
	}

	Argc<I> (*a1_)(void);
};

template<typename D, typename A1>
struct storage2_base: storage1<A1>
{
	typedef storage1<A1> base;
	using base::operator[];

	storage2_base(typename base::P1 p1) :
		base(p1)
	{
	}

	template<typename R, typename F, typename S>
	inline R operator()(type<R> , F &f, const S &s) const
	{
		const D &d = static_cast<const D&> (*this);
		return d[f](s[d.a1_], s[d.a2_]);
	}
};
template<typename A1, typename A2>
struct storage2: storage2_base<storage2<A1, A2> , A1>
{
	typedef storage2_base<storage2<A1, A2> , A1> base;
	typedef typename param_traits<A2>::type P2;
	typedef typename result_traits<A2>::type result_type;

	storage2(typename base::P1 p1, P2 p2) :
		base(p1), a2_(p2)
	{
	}

	using base::operator[];
	inline result_type operator[](Argc<2> (*)()) const
	{
		return a2_;
	}

	template<typename R, typename F>
	inline R Do(type<R> , F &f) const
	{
		return f(this->a1_, a2_);
	}

	A2 a2_;
};
template<typename A1, int I>
struct storage2<A1, Argc<I> (*)()> : storage2_base<storage2<A1, Argc<I> (*)()> , A1>
{
	typedef storage2_base<storage2<A1, Argc<I> (*)()> , A1> base;
	typedef typename param_traits<Argc<I> (*)()>::type P2;

	storage2(typename base::P1 p1, P2) :
		base(p1)
	{
	}

	Argc<I> (*a2_)(void);
};

template<typename D, typename A1, typename A2>
struct storage3_base: storage2<A1, A2>
{
	typedef storage2<A1, A2> base;
	using base::operator[];

	storage3_base(typename base::P1 p1, typename base::P2 p2) :
		base(p1, p2)
	{
	}

	template<typename R, typename F, typename S>
	inline R operator()(type<R> , F &f, const S &s) const
	{
		const D &d = static_cast<const D&> (*this);
		return d[f](s[d.a1_], s[d.a2_], s[d.a3_]);
	}
};
template<typename A1, typename A2, typename A3>
struct storage3: storage3_base<storage3<A1, A2, A3> , A1, A2>
{
	typedef storage3_base<storage3<A1, A2, A3> , A1, A2> base;
	typedef typename param_traits<A3>::type P3;
	typedef typename result_traits<A3>::type result_type;

	storage3(typename base::P1 p1, typename base::P2 p2, P3 p3) :
		base(p1, p2), a3_(p3)
	{
	}

	using base::operator[];
	inline result_type operator[](Argc<3> (*)()) const
	{
		return a3_;
	}

	template<typename R, typename F>
	inline R Do(type<R> , F &f) const
	{
		return f(this->a1_, this->a2_, a3_);
	}

	A3 a3_;
};
template<typename A1, typename A2, int I>
struct storage3<A1, A2, Argc<I> (*)()> : storage3_base<storage3<A1, A2, Argc<I> (*)()> , A1, A2>
{
	typedef storage3_base<storage3<A1, A2, Argc<I> (*)()> , A1, A2> base;
	typedef typename param_traits<Argc<I> (*)()>::type P3;

	storage3(typename base::P1 p1, typename base::P2 p2, P3) :
		base(p1, p2)
	{
	}
	Argc<I> (*a3_)(void);
};

template<typename D, typename A1, typename A2, typename A3>
struct storage4_base: storage3<A1, A2, A3>
{
	typedef storage3<A1, A2, A3> base;
	using base::operator[];

	storage4_base(typename base::P1 p1, typename base::P2 p2, typename base::P3 p3) :
		base(p1, p2, p3)
	{
	}

	template<typename R, typename F, typename S>
	inline R operator()(type<R> , F &f, const S &s) const
	{
		const D &d = static_cast<const D&> (*this);
		return d[f](s[d.a1_], s[d.a2_], s[d.a3_], s[d.a4_]);
	}
};
template<typename A1, typename A2, typename A3, typename A4>
struct storage4: storage4_base<storage4<A1, A2, A3, A4> , A1, A2, A3>
{
	typedef storage4_base<storage4<A1, A2, A3, A4> , A1, A2, A3> base;
	typedef typename param_traits<A4>::type P4;
	typedef typename result_traits<A4>::type result_type;

	using base::operator[];

	storage4(typename base::P1 p1, typename base::P2 p2, typename base::P3 p3, P4 p4) :
		base(p1, p2, p3), a4_(p4)
	{
	}

	inline result_type operator[](Argc<4> (*)()) const
	{
		return a4_;
	}

	template<typename R, typename F>
	inline R Do(type<R> , F &f) const
	{
		return f(this->a1_, this->a2_, this->a3_, a4_);
	}

	A4 a4_;
};
template<typename A1, typename A2, typename A3, int I>
struct storage4<A1, A2, A3, Argc<I> (*)()> : storage4_base<storage4<A1, A2, A3, Argc<I> (*)()> , A1, A2, A3>
{
	typedef storage4_base<storage4<A1, A2, A3, Argc<I> (*)()> , A1, A2, A3> base;
	typedef typename param_traits<Argc<I> (*)()>::type P4;

	storage4(typename base::P1 p1, typename base::P2 p2, typename base::P3 p3, P4) :
		base(p1, p2, p3)
	{
	}
	Argc<I> (*a4_)(void);
};

namespace _bind
{

template<typename R, typename F>
struct f_b
{
	typedef typename result_traits<R>::type result_type;
	explicit f_b(const F &f) :
		f_(f)
	{
	}
	F f_;
};

template<typename R, typename C, typename F>
struct f_0: f_b<R, F>
{
	typedef f_b<R, F> base;

	explicit f_0(const F &f) :
		base(f)
	{
	}

	template<typename U>
	inline typename base::result_type operator()(U &u) const
	{
		return (get_pointer(u)->*base::f_)();
	}

	inline typename base::result_type operator()(C &c) const
	{
		return (c.*base::f_)();
	}
};

template<typename R, typename C, typename F>
struct f_1: f_b<R, F>
{
	typedef f_b<R, F> base;

	explicit f_1(const F &f) :
		base(f)
	{
	}

	template<typename U, typename A1>
	inline typename base::result_type operator()(U &u, A1 a1) const
	{
		return (get_pointer(u)->*base::f_)(a1);
	}

	template<typename A1>
	inline typename base::result_type operator()(C &c, A1 a1) const
	{
		return (c.*base::f_)(a1);
	}
};

template<typename R, typename C, typename F>
struct f_2: f_b<R, F>
{
	typedef f_b<R, F> base;

	explicit f_2(const F &f) :
		base(f)
	{
	}

	template<typename U, typename A1, typename A2>
	inline typename base::result_type operator()(U &u, A1 a1, A2 a2) const
	{
		return (get_pointer(u)->*base::f_)(a1, a2);
	}

	template<typename A1, typename A2>
	inline typename base::result_type operator()(C &c, A1 a1, A2 a2) const
	{
		return (c.*base::f_)(a1, a2);
	}
};

template<typename R, typename C, typename F>
struct f_3: f_b<R, F>
{
	typedef f_b<R, F> base;

	explicit f_3(const F &f) :
		base(f)
	{
	}

	template<typename U, typename A1, typename A2, typename A3>
	inline typename base::result_type operator()(U &u, A1 a1, A2 a2, A3 a3) const
	{
		return (get_pointer(u)->*base::f_)(a1, a2, a3);
	}

	template<typename A1, typename A2, typename A3>
	inline typename base::result_type operator()(C &c, A1 a1, A2 a2, A3 a3) const
	{
		return (c.*base::f_)(a1, a2, a3);
	}
};

}

template<typename R, typename F, typename S> struct bind_t
{
public:
	typedef typename result_traits<R>::type result_type;

	bind_t(const F &f, const S &s) :
		f_(f), s_(s)
	{
	}

	template<typename A>
	inline result_type eval(A &a)
	{
		return s_(type<result_type> (), f_, a);
	}
	template<typename A>
	inline result_type eval(A &a) const
	{
		return s_(type<result_type> (), f_, a);
	}

	inline result_type operator()()
	{
		typedef storage0 ll;
		return s_(type<result_type> (), f_, ll());
	}
	inline result_type operator()() const
	{
		typedef storage0 ll;
		return s_(type<result_type> (), f_, ll());
	}

	template<typename P1>
	inline result_type operator()(P1 &p1)
	{
		typedef storage1<P1&> ll;
		return l_(type<result_type> (), f_, ll(p1));
	}
	template<typename P1>
	inline result_type operator()(P1 &p1) const
	{
		typedef storage1<P1&> ll;
		return l_(type<result_type> (), f_, ll(p1));
	}
	template<typename P1>
	inline result_type operator()(const P1 &p1)
	{
		typedef storage1<const P1&> ll;
		return l_(type<result_type> (), f_, ll(p1));
	}
	template<typename P1>
	inline result_type operator()(const P1 &p1) const
	{
		typedef storage1<const P1&> ll;
		return l_(type<result_type> (), f_, ll(p1));
	}
	template<typename P1, typename P2>
	inline result_type operator()(const P1 &p1, const P2 &p2) const
	{
		typedef storage2<const P1&, const P2&> ll;
		return s_(type<result_type> (), f_, ll(p1, p2));
	}
	template<typename P1, typename P2, typename P3>
	inline result_type operator()(const P1 &p1, const P2 &p2, const P3 &p3) const
	{
		typedef storage3<const P1&, const P2&, const P3&> ll;
		return s_(type<result_type> (), f_, ll(p1, p2, p3));
	}
	template<typename P1, typename P2, typename P3, typename P4>
	inline result_type operator()(const P1 &p1, const P2 &p2, const P3 &p3, const P4 &p4) const
	{
		typedef storage4<const P1&, const P2&, const P3&, const P4&> ll;
		return s_(type<result_type> (), f_, ll(p1, p2, p3, p4));
	}

private:

	F f_;
	S s_;
};

template<typename R>
bind_t<R, R(*)(), storage0> bind(R(*f)())
{
	typedef R (*F)();
	typedef storage0 S;
	return bind_t<R, F, S> (f, S());
}
;
template<typename R, typename P1, typename A1>
bind_t<R, R(*)(P1), storage1<A1> > bind(R(*f)(P1), A1 a1)
{
	typedef R (*F)(P1);
	typedef storage1<A1> S;
	return bind_t<R, F, S> (f, S(a1));
}
template<typename R, typename P1, typename P2, typename A1, typename A2>
bind_t<R, R(*)(P1, P2), storage2<A1, A2> > bind(R(*f)(P1, P2), A1 a1, A2 a2)
{
	typedef R (*F)(P1, P2);
	typedef storage2<A1, A2> S;
	return bind_t<R, F, S> (f, S(a1, a2));
}
template<typename R, typename P1, typename P2, typename P3, typename A1, typename A2, typename A3>
bind_t<R, R(*)(P1, P2, P3), storage3<A1, A2, A3> > bind(R(*f)(P1, P2, P3), A1 a1, A2 a2, A3 a3)
{
	typedef R (*F)(P1, P2, P3);
	typedef storage3<A1, A2, A3> S;
	return bind_t<R, F, S> (f, S(a1, a2, a3));
}
template<typename R, typename P1, typename P2, typename P3, typename P4, typename A1, typename A2, typename A3, typename A4>
bind_t<R, R(*)(P1, P2, P3, P4), storage4<A1, A2, A3, A4> > bind(R(*f)(P1, P2, P3, P4), A1 a1, A2 a2, A3 a3, A4 a4)
{
	typedef R (*F)(P1, P2, P3, P4);
	typedef storage4<A1, A2, A3, A4> S;
	return bind_t<R, F, S> (f, S(a1, a2, a3, a4));
}

template<typename R, typename C, typename C1>
bind_t<R, _bind::f_0<R, C, R(C::*)()>, storage1<C1> > bind(R(C::*f)(), C1 c1)
{
	typedef _bind::f_0<R, C, R(C::*)()> F;
	typedef storage1<C1> S;
	return bind_t<R, F, S> (F(f), S(c1));
}
template<typename R, typename C, typename C1>
bind_t<R, _bind::f_0<R, C, R(C::*)() const>, storage1<C1> > bind(R(C::*f)() const, C1 c1)
{
	typedef _bind::f_0<R, C, R(C::*)() const> F;
	typedef storage1<C1> S;
	return bind_t<R, F, S> (F(f), S(c1));
}
template<typename R, typename C, typename A1, typename C1, typename P1>
bind_t<R, _bind::f_1<R, C, R(C::*)(A1)>, storage2<C1, P1> > bind(R(C::*f)(A1), C1 c1, P1 p2)
{
	typedef _bind::f_1<R, C, R(C::*)(A1)> F;
	typedef storage2<C1, P1> S;
	return bind_t<R, F, S> (F(f), S(c1, p2));
}
template<typename R, typename C, typename A1, typename C1, typename P1>
bind_t<R, _bind::f_1<R, C, R(C::*)(A1)>, storage2<C1, P1> > bind(R(C::*f)(A1) const, C1 c1, P1 p2)
{
	typedef _bind::f_1<R, C, R(C::*)(A1) const> F;
	typedef storage2<C1, P1> S;
	return bind_t<R, F, S> (F(f), S(c1, p2));
}
template<typename R, typename C, typename A1, typename A2, typename C1, typename P1, typename P2>
bind_t<R, _bind::f_2<R, C, R(C::*)(A1, A2)>, storage3<C1, P1, P2> > bind(R(C::*f)(A1, A2), C1 c1, P1 p1, P2 p2)
{
	typedef _bind::f_2<R, C, R(C::*)(A1, A2)> F;
	typedef storage3<C1, P1, P2> S;
	return bind_t<R, F, S> (F(f), S(c1, p1, p2));
}
template<typename R, typename C, typename A1, typename A2, typename C1, typename P1, typename P2>
bind_t<R, _bind::f_2<R, C, R(C::*)(A1, A2) const>, storage3<C1, P1, P2> > bind(R(C::*f)(A1, A2) const, C1 c1, P1 p1, P2 p2)
{
	typedef _bind::f_2<R, C, R(C::*)(A1, A2) const> F;
	typedef storage3<C1, P1, P2> S;
	return bind_t<R, F, S> (F(f), S(c1, p1, p2));
}
template<typename R, typename C, typename A1, typename A2, typename A3, typename C1, typename P1, typename P2, typename P3>
bind_t<R, _bind::f_3<R, C, R(C::*)(A1, A2, A3)>, storage4<C1, P1, P2, P3> > bind(R(C::*f)(A1, A2, A3), C1 c1, P1 p1, P2 p2, P3 p3)
{
	typedef _bind::f_3<R, C, R(C::*)(A1, A2, A3)> F;
	typedef storage4<C1, P1, P2, P3> S;
	return bind_t<R, F, S> (F(f), S(c1, p1, p2, p3));
}
template<typename R, typename C, typename A1, typename A2, typename A3, typename C1, typename P1, typename P2, typename P3>
bind_t<R, _bind::f_3<R, C, R(C::*)(A1, A2, A3) const>, storage4<C1, P1, P2, P3> > bind(R(C::*f)(A1, A2, A3) const, C1 c1, P1 p1, P2 p2, P3 p3)
{
	typedef _bind::f_3<R, C, R(C::*)(A1, A2, A3) const> F;
	typedef storage4<C1, P1, P2, P3> S;
	return bind_t<R, F, S> (F(f), S(c1, p1, p2, p3));
}

}//namespace tlib

#endif
